#include "syntaxvalidator.h"
/*
0 Not exist 
1 Exist
Validate the syntax tree with all the e-llicite rules
*/
int validateSyntax(ASTTREE tree,SYMTREE sym)
{
	
	int type = tree->type;
	SYMTREE node;

	TraceMsg("validateSyntax",humanReadableNodeType(type));
	TraceNb("validateSyntax",type);
	switch(type) 
	{
		case AT_PROG : return validateSyntax(tree->right,sym);
		case AT_FUNC :
			if ( tree->left != NULL){
				node = searchFunctionDefinition(sym,tree);

				if(node == NULL)return KO;
				if ( IsFuncContainsReturnStatement(tree->right,node,tree->left->vtype) != OK) return KO;
				if(tree->right != NULL)
					if(validateSyntax(tree->right,node)!= 0) return KO;
				return validateSyntax(tree->left,node);
			}else
				return OK;
		case AT_FUNC_HEAD: 
            if(tree->left == NULL) return OK;
            return validateInstructionList(tree->left,sym,AT_UNKNOWN);
		
		case AT_FUNC_BODY:
			if ( validateSyntax(tree->left,sym) == 0)
			{
				return validateSyntax(tree->right,sym);
			}
			else return KO;
			
		case AT_DECLARE:
            if ( tree->vtype == AT_VOID) return KO;
			if ( tree->left != NULL )
			{
				if ( validateSyntax(tree->left,sym) == OK)
				{
					return validateSyntax(tree->right,sym);
				}
				else return KO;
			}
			else
			{
				return validateSyntax(tree->right,sym);
			}
		case AT_DECLARE_BLOC:
			if (tree->left != NULL)
				return validateSyntax(tree->left,sym);
			else return OK;
		case AT_IMPL:
            if(tree->left != NULL)
                return validateInstructionList(tree->left,sym,AT_UNKNOWN);
            else return OK;
		case AT_ID: 
			/*printf(";%d %s %d %d ",tree->vtype,tree->sval,tree->ival , tree->bval);
			node = alreadyIsSymbol(sym, tree->sval);
			if (node == NULL)
			{
				return Error(2,"The variable is not defined");
			}
			else
			{
				printf("Found node with vtype %d\n", node->idType);
			}*/
			return OK;
		default: 
			return Error(111,"validateSyntax Unknown node");
	}

}


int validateInstructionList(ASTTREE tree,SYMTREE sym, int currentType)
{
    SYMTREE symNode = NULL;
    int leftType;
    int rightType;
    int result;
	TraceMsg("validateInstructionList",humanReadableNodeType(tree->type));
	switch(tree->type)
	{
        case AT_ID: 
			if ( GetType(tree,sym) != KO /*&& GetType(tree,sym) == currentType*/) return OK;
			else return Error(-102,"Missing declared variable");
		case AT_NB: 
			if ( currentType == AT_NB) return OK;
			else return KO;
		case AT_BOOL:
			if ( currentType == AT_BOOL) return OK;
			else return KO;
			
        case AT_ILIST:
			if ( tree->left != NULL)
			{
				if ( currentType == AT_UNKNOWN) currentType = tree->left->vtype;
					result = validateInstructionList(tree->left,sym,currentType);
					//printf("Result L %d \n",result);
					if ( result == KO)
						return result;
			}
			
			if ( tree->right != NULL)
            {
				if ( currentType == AT_UNKNOWN) currentType = tree->right->vtype;
                result = validateInstructionList(tree->right,sym,currentType);
				//printf("Result R %d \n",result);
					return result;
            }
            else
                return KO;

		case AT_OPADD:
            
            //printf("type left %d = type righ %d", GetType(tree->left,sym), GetType(tree->right,sym));
			if ( currentType == AT_UNKNOWN) currentType = AT_NB;
			if ( currentType != AT_NB) return Error(-100,"Could not assign");

			leftType = GetType(tree->right,sym);
			rightType = GetType(tree->left,sym);
			
			if ( leftType == KO || rightType == KO || leftType == AT_BOOL || rightType == AT_BOOL)
				return KO;
				
			if ( leftType == AT_NB && rightType == AT_NB)
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_NB) == OK)
					return validateInstructionList(tree->left, sym, AT_NB);
			}
        case AT_OPMUL:
			if ( currentType == AT_UNKNOWN) currentType = AT_NB;
			if ( currentType != AT_NB) return Error(-100,"Could not assign");
			
			leftType = GetType(tree->right,sym);
			rightType = GetType(tree->left,sym);
			if ( leftType == KO || rightType == KO || leftType == AT_BOOL || rightType == AT_BOOL)
				return KO;
				
			if ( leftType == AT_NB && rightType == AT_NB)
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_NB) == OK)
					return validateInstructionList(tree->left, sym, AT_NB);
			}
        case AT_OPDIV:
			if ( currentType == AT_UNKNOWN) currentType = AT_NB;
			if ( currentType != AT_NB) return Error(-100,"Could not assign");
			
			leftType = GetType(tree->right,sym);
			rightType = GetType(tree->left,sym);
			if ( leftType == KO || rightType == KO || leftType == AT_BOOL || rightType == AT_BOOL)
				return KO;
				
			if ( leftType == AT_NB && rightType == AT_NB)
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_NB) == OK)
					return validateInstructionList(tree->left, sym, AT_NB);
			}
        case AT_OPSUB:
			if ( currentType == AT_UNKNOWN) currentType = AT_NB;
			if ( currentType != AT_NB) return Error(-100,"Could not assign");
			
			leftType = GetType(tree->right,sym);
			rightType = GetType(tree->left,sym);
			if ( leftType == KO || rightType == KO || leftType == AT_BOOL || rightType == AT_BOOL)
				return KO;
				
			if ( leftType == AT_NB && rightType == AT_NB)
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_NB) == OK)
					return validateInstructionList(tree->left, sym, AT_NB);
			}
        case AT_OR:
			if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-100,"Could not assign");
			
			leftType = GetType(tree->right,sym);
			rightType = GetType(tree->left,sym);
			if ( leftType == KO || rightType == KO || leftType == AT_NB || rightType == AT_NB)
				return KO;
				
			if ( leftType == AT_BOOL && rightType == AT_BOOL)
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_BOOL) == OK)
					return validateInstructionList(tree->left, sym, AT_BOOL);
			}
        case AT_AND:
			if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-100,"Could not assign");
			
			leftType = GetType(tree->right,sym);
			rightType = GetType(tree->left,sym);
			if ( leftType == KO || rightType == KO || leftType == AT_NB || rightType == AT_NB)
				return KO;
				
			if ( leftType == AT_BOOL && rightType == AT_BOOL)
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_BOOL) == OK)
					return validateInstructionList(tree->left, sym, AT_BOOL);
			}
        case AT_NOT:
			if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-100,"Could not assign");
			
			rightType = GetType(tree->right,sym);
			if ( rightType == KO || rightType == AT_NB)
				return KO;
				
			if ( rightType == AT_BOOL)
				return OK;
			else
			{
				return validateInstructionList(tree->right, sym, AT_BOOL);
			}
        case AT_LT: //TODO manage complex element
			if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-100,"Could not assign");

			if ( GetType(tree->left,sym) == AT_NB && GetType(tree->right,sym) == AT_NB)
				return OK;
			else
				return KO;
        case AT_LE:
			if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-100,"Could not assign");
				
			if ( GetType(tree->left,sym) == AT_NB && GetType(tree->right, sym) == AT_NB)
				return OK;
			else
				return KO;
        case AT_OPEQ:
			if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-100,"Could not assign");
			
			rightType = GetType(tree->right,sym);
			leftType = GetType(tree->left,sym);
			
			if ( leftType == KO || rightType == KO || (leftType == AT_NB && rightType == AT_BOOL) || (leftType == AT_BOOL && rightType == AT_NB))
				return KO;
				
			if (( leftType == AT_BOOL && rightType == AT_BOOL) || (leftType == AT_NB && rightType == AT_NB))
				return OK;
			else
			{
				if ( validateInstructionList(tree->right, sym, AT_UNKNOWN) == OK)
					return validateInstructionList(tree->left, sym, AT_UNKNOWN);
			}
        case AT_NEG:
			if ( GetType(tree->right,sym) == AT_NB )
				return OK;
			if ( GetType(tree->right,sym) == AT_UNKNOWN )
				return validateInstructionList(tree->right,sym,AT_NB);
		case AT_OPASS:
			leftType = GetType(tree->left,sym);
			if ( leftType == KO) return Error(1,"Missing declared variable");	// Var is not in the declared bloc
			return validateInstructionList(tree->right,sym,leftType);
		case AT_OPWRITE:
				return validateInstructionList(tree->left,sym,tree->left->vtype);
		case AT_OPREAD:
			if (GetType(tree->left,sym) != KO && GetType(tree->left,sym) != AT_UNKNOWN)
				return OK;
			else return KO;
		case AT_RETURN:
			return OK;
        case AT_FUNC:
			if ( tree->left->vtype != AT_VOID)
				if ( IsFuncContainsReturnStatement(tree->right,sym,tree->left->vtype) == OK)
					return OK;
				else return KO;
        case AT_IF:
            if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-118,"Invalid Type");
			
			leftType = GetType(tree->left,sym);
			if ( leftType == KO || leftType == AT_NB)
				return KO;
            
			if ( leftType == AT_BOOL )
				return OK;
			else
			{
				if ( validateInstructionList(tree->left, sym, AT_BOOL) == OK)
                {
                    if(tree->right != NULL)//if body empty or not
                        return validateInstructionList(tree->right, sym, AT_UNKNOWN);
                    else return OK; 
                    
                }
                else
                    return KO;
			}
        case AT_IF_BODY:
            if(tree->right != NULL)//if body empty or not
                if( validateInstructionList(tree->right, sym, AT_UNKNOWN)==OK)
                    if(tree->left != NULL)//if body empty or not
                       return validateInstructionList(tree->left, sym, AT_UNKNOWN);
                    else
                       return OK;
                else
                    return KO;
            else
                if(tree->left != NULL)//if body empty or not
                    return validateInstructionList(tree->left, sym, AT_UNKNOWN);
                else
                    return OK;
                
        case AT_WHILE:
            if ( currentType == AT_UNKNOWN) currentType = AT_BOOL;
			if ( currentType != AT_BOOL) return Error(-119,"Invalid Type");
			
			leftType = GetType(tree->left,sym);
			if ( leftType == KO || leftType == AT_NB)
				return KO;
            
			if ( leftType == AT_BOOL )
				return OK;
			else
			{
				if ( validateInstructionList(tree->left, sym, AT_BOOL) == OK)
                {
                    if(tree->right != NULL)//if body empty or not
                        return validateInstructionList(tree->right, sym, AT_UNKNOWN);
                    else return OK; 
                        
                }
                else
                    return KO;
			}
        case AT_ARGS:
            //void not allowed as a type
            TraceMsg("Argument validation",tree->left->sval);
            if(tree->vtype == AT_VOID)return KO;
            if(tree->right != NULL)
                return validateInstructionList(tree->right, sym, AT_UNKNOWN);
            return OK;
        case AT_ISET:
            return OK;
        case AT_VOID:
            return OK;
        case AT_FUNC_CALL:	

			if (IsFuncDefined(tree,sym,currentType)==KO)
				return KO;
				
			//validate args for the call
            symNode = getFunctionDefinition(tree,sym);
			
			//if ( currentType != AT_UNKNOWN && symNode->type == currentType) 
				return validateArgs(tree->right,symNode->args, sym);
		//	else return KO;
			
            
		default:
			printf("%d",tree->type);
			return Error(-100,"No case for this node");
	}
	return OK;
}

int validateArgs(ASTTREE callArgs, SYMTREE definitionArgs, SYMTREE sym)
{
    TraceMsg("validate args","");
    // no arguments return OK
    if(callArgs == NULL && definitionArgs == NULL) return OK;
    // only one call has arguments return KO
    if(callArgs == NULL ^ definitionArgs == NULL) return Error(-122,"Invalid number of arguments");
    //if first args have different type return KO
    if(validateInstructionList(callArgs->left,sym,definitionArgs->typeVal)==KO)
        return Error(-123,"Invalid argument type");
    //otherwise validate the rest of arguments
    return validateArgs(callArgs->right, definitionArgs->next, sym);

}
int GetType(ASTTREE tree,SYMTREE sym)
{
	if ( tree->type == AT_ID )
	{
		TraceMsg2("GetType"," AT_ID check",tree->sval);
		SYMTREE symNode = searchForVarOrArgWithoutType(sym, tree->sval);
		if ( symNode == NULL)
			return KO;
		else {
		printf("type %d\n",symNode->typeVal);
		return symNode->typeVal;
		}
	}
	else if(tree->type == AT_BOOL || tree->type == AT_NB || tree->type == AT_NEG)
    {
		return tree->vtype;
    }
	else if(tree->type == AT_FUNC_CALL)
	{
		SYMTREE symNode = searchFunctionCalled(sym,tree);
		if ( symNode == NULL )
			return KO;
		else
			return symNode->typeVal;
	}
    else 
    {
        return AT_UNKNOWN;
	}
}



int IsFuncDefined(ASTTREE tree,SYMTREE sym)
{
        SYMTREE symNode = NULL;
		symNode = getFunctionDefinition(tree,sym);	//-1: ignore the scope
		TraceMsg2("IsFuncDefined","Function ",(symNode==NULL)?"is not found":"is found");
		if ( symNode == NULL)
		{
			return KO;
			}
		else 
		{
			return OK;
		}
}

SYMTREE getFunctionDefinition(ASTTREE tree,SYMTREE sym){
    SYMTREE symNode = NULL;

    if ( tree->left->type == AT_ID )
	{
        symNode = searchFunctionCalled(sym,tree);	//-1: ignore the scope
        return symNode;
    }
    return symNode;
 
}

/*Entering by the BODY*/
int IsFuncContainsReturnStatement(ASTTREE tree,SYMTREE sym, int retType)
{
	return OK;
	/*if ( retType == AT_VOID)
		return OK;
		
	if ( tree != NULL)
	{
		switch(tree->type)
		{
			case AT_FUNC_BODY: return IsFuncContainsReturnStatement(tree->right,sym, retType);
			case AT_IMPL: return IsFuncContainsReturnStatement(tree->left,sym, retType);
			case AT_ILIST: return IsFuncContainsReturnStatement(tree->right,sym, retType);
			case AT_RETURN: return validateInstructionList(tree->left,sym,retType);
			default: return Error(110,"IsFuncContainsReturnStatement: UNKNOWN node");
		}
	}
	
	return Error(110,"No return found");*/
}

